package pl.druminski.robinhood.model;

import java.io.File;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @author Łukasz Drumiński
 * 
 */
public class Data implements Serializable
{
    //separator pliku, w windows \ w unix /
    private String separator = System.getProperty("file.separator")+System.getProperty("file.separator");

    //wersja programu
    private double version = 0.2;
    //rodzaj aktualizacji aplikacji i pluginow
    private UpdatingInfo updating = UpdatingInfo.MANUAL;
    //sciezka do pluginow wzgledem katalogu glownego programu
    private String dirPluginName = "." + separator + "Plugins";
    //co ile milisekund ma być sprawdzane czy jest połączenie z internetem przy starcie proramu
    private int sleep = 6000;
    //nazwa pakietu pluginu
    private String packagePluginName = "plugin";
    //nazwa logu
    private String logName = "RobinHoodLog";
    //ikona do tray
    private ImageIcon icon;
    //adres pliku xml o aktualnych informacjach programu
    private URL updateAddress;
    //adres lokalnego pliku xml z informacjami o plikach do pobrania
    private File updateFile = new File("UpdateInfo.xml");
    //sciezka do katalogu z bibliotekami
    private String libPath = "." + separator + "lib";
    //nazwa pliku konfiguracyjnego
    private File configFile = new File("Config.xml");
    //sciezka do katalogu glownego
    private String rootPath;
    //uruchamianie programu przy starcie systemu
    private Boolean runWithSystemStart = false;
    //informacje o pluginach
    public ArrayList<PluginInfo> pluginInfo = new ArrayList<PluginInfo>();
    //lista adresow proxy
    public ArrayList<Proxy> proxyList = new ArrayList<Proxy>();
    //ilosc minut od uzyskania polaczenia internetowego do przeprowadzenia symulacji klikniec
    private int startTime = 0;
    //ilosc minut po ktorych program ma zostac wylaczony po przeprowadzeniu symulacji klikniec
    private int exitTime = 1;

    public Data()
    {
        try
        {
            updateAddress = new URL("http://www.druminski.ovh.org/RobinHood/RobinHoodInfo.xml");

        } catch (MalformedURLException ex)
        {
            Logger.getLogger(this.getLogName()).log(Level.SEVERE, null, ex);
        }
        icon = new ImageIcon(getClass().getResource("/pl/druminski/robinhood/graphics/heart.png"));

        rootPath = new File(".").getAbsolutePath();
        rootPath = rootPath.substring(0, rootPath.length() - 1);

    }

    public String getDirPluginName()
    {
        return dirPluginName;
    }

    public int getSleep()
    {
        return sleep;
    }

    public String getPackagePluginName()
    {
        return packagePluginName;
    }

    public String getLogName()
    {
        return logName;
    }

    public double getVersion()
    {
        return version;
    }

    public UpdatingInfo getUpdatingMethod()
    {
        return updating;
    }

    public void setUpdatingMethod(UpdatingInfo method)
    {
        updating = method;
    }

    public ImageIcon getIcon()
    {
        return icon;
    }

    public URL getInfoAddress()
    {
        return updateAddress;
    }

    public File getUpdateFile()
    {
        return updateFile;
    }

    public String getLibPath()
    {
        return libPath;
    }

    public Boolean getRunWithSystemStart()
    {
        return runWithSystemStart;
    }

    public void setRunWithSystemStart(Boolean run)
    {
        runWithSystemStart = run;
    }

    public String getRootPath()
    {
        return rootPath;
    }

    public File getConfigFile()
    {
        return configFile;
    }

    public void addProxy(String address, int port, Proxy.Type proxyType)
    {
        Proxy proxy = new Proxy(proxyType, new InetSocketAddress(address, port));
        proxyList.add(proxy);
    }

    public boolean isLoadablePlugin(String name)
    {
        boolean load = true;
        for(int i=0; i < this.pluginInfo.size(); i++)
        {
            PluginInfo info = pluginInfo.get(i);
            if (info.getName().equals(name))
            {
                load = info.load();
                break;
            }
        }

        return load;
    }

    public void addClicks(String pluginName, int clicks)
    {
    for(int i=0; i < this.pluginInfo.size(); i++)
        {
            PluginInfo info = pluginInfo.get(i);
            if (info.getName().equals(pluginName))
            {
                info.addClicks(clicks);
                break;
            }
        }
    }

    public void setStartTime(int time)
    {
        startTime = time;
    }

    public void setExitTime(int time)
    {
        exitTime = time;
    }

    public int getStartTime()
    {
        return startTime;
    }

    public int getExitTime()
    {
        return exitTime;
    }

    public String getSeparator()
    {
        return separator;
    }

    public void loadConfig()
    {
        XPathFactory xpfactory = XPathFactory.newInstance();
        XPath path = xpfactory.newXPath();

        DocumentBuilderFactory factory;
        DocumentBuilder builder;
        Document doc = null;

        Boolean ok = true;

        if (!this.getConfigFile().exists())
            ok = false;
        else
        {
            factory = DocumentBuilderFactory.newInstance();
            try
            {
                builder = factory.newDocumentBuilder();
                doc = builder.parse(this.getConfigFile());
            } catch (Exception ex)
            {
                ok = false;
                Logger.getLogger(this.getLogName()).warning("Problem z parserem xml. " + ex.getMessage());
            }
        }

        if (ok)
        {
            try
            {
                String tmp = path.evaluate("/configuration/update", doc);
                if (!tmp.equals(""))
                {
                    int update = Integer.parseInt(tmp);

                    switch (update)
                    {
                        case 0:
                            updating = UpdatingInfo.AUTO;
                            break;
                        case 1:
                            updating = UpdatingInfo.MANUAL;
                            break;
                        case 2:
                            updating = UpdatingInfo.NONE;
                            break;
                    }
                }

                tmp = path.evaluate("/configuration/startTime", doc);
                if (!tmp.equals(""))
                {
                    startTime = Integer.parseInt(tmp);
                }

                tmp = path.evaluate("/configuration/exitTime", doc);
                if (!tmp.equals(""))
                {
                    exitTime = Integer.parseInt(tmp);
                }

                tmp = path.evaluate("/configuration/autostart", doc);
                if (!tmp.equals(""))
                {
                    if (tmp.equals("1")) runWithSystemStart = true;
                    else runWithSystemStart = false;
                }


                int count = ((Number) path.evaluate("count(/configuration/proxyList/proxy)", doc, XPathConstants.NUMBER)).intValue();

                if (count > 0)
                {
                    String address;
                    String sPort;
                    String sType;
                    int port;
                    int type;
                    for (int i = 1; i <= count; i++)
                    {
                        address = path.evaluate("/configuration/proxyList/proxy[" + i + "]/address", doc);
                        sPort = path.evaluate("/configuration/proxyList/proxy[" + i + "]/port", doc);
                        sType = path.evaluate("/configuration/proxyList/proxy[" + i + "]/type", doc);
                        port = Integer.parseInt(sPort);
                        type = Integer.parseInt(sType);

                        Proxy.Type proxyType = Proxy.Type.HTTP;
                        if (type == 1)
                        {
                            proxyType = Proxy.Type.SOCKS;
                        }

                        this.addProxy(address, port, proxyType);

                    }
                }

                count = ((Number) path.evaluate("count(/configuration/pluginList/plugin)", doc, XPathConstants.NUMBER)).intValue();

                if (count > 0)
                {
                    String name = null;
                    Boolean load = false;
                    int clicks = 0;
                    int ageDay = 0;
                    int ageMonth = 0;
                    int ageYear = 0;
                    String k;
                    PluginInfo info;
                    
                    for (int i = 1; i <= count; i++)
                    {
                        name = path.evaluate("/configuration/pluginList/plugin[" + i + "]/name", doc);
                        k = path.evaluate("/configuration/pluginList/plugin[" + i + "]/load", doc);
                        if (k.equals("1")) load = true;
                        k = path.evaluate("/configuration/pluginList/plugin[" + i + "]/clicks", doc);
                        clicks = Integer.parseInt(k);
                        k = path.evaluate("/configuration/pluginList/plugin[" + i + "]/age/day", doc);
                        ageDay = Integer.parseInt(k);
                        k = path.evaluate("/configuration/pluginList/plugin[" + i + "]/age/month", doc);
                        ageMonth = Integer.parseInt(k);
                        k = path.evaluate("/configuration/pluginList/plugin[" + i + "]/age/year", doc);
                        ageYear = Integer.parseInt(k);

                        info = new PluginInfo(name, load, clicks);
                        info.setAge(ageDay, ageMonth, ageYear);

                        pluginInfo.add(info);
                    }
                    
                }


            } catch (XPathExpressionException ex)
            {
                Logger.getLogger(this.getLogName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void saveConfig()
    {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = null;
        try
        {
            builder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException ex)
        {
            Logger.getLogger(this.logName).log(Level.SEVERE, null, ex);
        }
        DOMImplementation impl = builder.getDOMImplementation();

        Document doc = impl.createDocument(null, null, null);
        Element root = doc.createElement("configuration");
        doc.appendChild(root);

        Element el;
        Element child;
        
        //updating
        el = doc.createElement("update");
        int k=0;
        if (this.updating == UpdatingInfo.MANUAL) k = 1;
        else if (this.updating == UpdatingInfo.NONE) k = 2;
        el.setTextContent("" + k);
        root.appendChild(el);

        //startTime
        el = doc.createElement("startTime");
        el.setTextContent("" + startTime);
        root.appendChild(el);

        //exitTime
        el = doc.createElement("exitTime");
        el.setTextContent("" + exitTime);
        root.appendChild(el);

        //autostart
        String autostart = "1";
        if (!runWithSystemStart) autostart = "0";
        el = doc.createElement("autostart");
        el.setTextContent(autostart);
        root.appendChild(el);



        //proxyList
        if (this.proxyList.size() > 0)
        {
            el = doc.createElement("proxyList");
            Element elType;
            Element elAddress;
            Element elPort;
            String t;
            InetSocketAddress addr;

            for(int i=0; i < proxyList.size(); i++)
            {
                Proxy proxy = proxyList.get(i);
                child = doc.createElement("proxy");
                
                elType = doc.createElement("type");
                elAddress = doc.createElement("address");
                elPort = doc.createElement("port");
                
                if (proxy.type() == Proxy.Type.SOCKS) t = "1";
                else t = "0";
                elType.setTextContent(t);

                addr = (InetSocketAddress) proxy.address();
                elAddress.setTextContent(addr.getHostName());
                elPort.setTextContent("" + addr.getPort());

                child.appendChild(elType);
                child.appendChild(elAddress);
                child.appendChild(elPort);

                el.appendChild(child);
            }

            root.appendChild(el);
        }

        //pluginList
        if (pluginInfo.size() > 0)
        {
            el = doc.createElement("pluginList");

            Element elName;
            Element elLoad;
            Element elClicks;
            Element elAge;
            Element elAgeDay;
            Element elAgeMonth;
            Element elAgeYear;

            String tmp;

            for(int i=0; i<pluginInfo.size(); i++)
            {
                PluginInfo info = pluginInfo.get(i);

                child = doc.createElement("plugin");

                elName = doc.createElement("name");
                elLoad = doc.createElement("load");
                elClicks = doc.createElement("clicks");
                elAge = doc.createElement("age");
                elAgeDay = doc.createElement("day");
                elAgeMonth = doc.createElement("month");
                elAgeYear = doc.createElement("year");

                elName.setTextContent(info.getName());

                tmp = "0";
                if (info.load()) tmp = "1";
                elLoad.setTextContent(tmp);

                elClicks.setTextContent("" + info.getClicks());

                elAgeDay.setTextContent("" + info.getAge().getDay());
                elAgeMonth.setTextContent("" + info.getAge().getMonth());
                elAgeYear.setTextContent("" + info.getAge().getYear());
                elAge.appendChild(elAgeDay);
                elAge.appendChild(elAgeMonth);
                elAge.appendChild(elAgeYear);
                
                child.appendChild(elName);
                child.appendChild(elLoad);
                child.appendChild(elClicks);
                child.appendChild(elAge);
                
                el.appendChild(child);

            }
            root.appendChild(el);
        }
        
        
        // transform the Document into a String
        DOMSource domSource = new DOMSource(doc);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = null;
        try
        {
            transformer = tf.newTransformer();
        } catch (TransformerConfigurationException ex)
        {
            Logger.getLogger(this.logName).log(Level.SEVERE, null, ex);
        }
        //transformer.setOutputProperty (OutputKeys.OMIT_XML_DECLARATION, "yes");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        try
        {
            transformer.transform(domSource, new StreamResult(new FileOutputStream(this.getConfigFile())));
        } catch (Exception ex)
        {
            Logger.getLogger(Data.class.getName()).log(Level.SEVERE, null, ex);
        }


    }
}
